import { v4 as uid } from "uuid";
// @ts-ignore
import __resolvePathname from "resolve-pathname";
import { getFirstVariable, parseImportDeclaration } from "@/utils";
import {
  ParsedRegularExp,
  ParsedResource,
  ParsedTemplate,
  Resource,
  Workspace,
  RawWorkspace,
  RawMultiWorkspace,
} from "@/types";
import validate from "./validate";
import { useWorkspace } from "@/features";
import { mapToSystemDriver } from "./drivers";

const ENTRY_PATH = "/workspace.json";
const WEB_BASE_URL = `${window.location.origin}${import.meta.env.BASE_URL}`;
const INDEX_HTML_PATH = window.location.pathname.replace(
  import.meta.env.BASE_URL,
  ""
);

// just for ts-check
function resolvePathname(path: string, base: string): string {
  return __resolvePathname(path, base);
}

const ABSURL = (path: string) => `${WEB_BASE_URL}/${path}`;

export async function bootstrap() {
  const realEntryPath = resolvePathname(ENTRY_PATH, INDEX_HTML_PATH);
  const entryURL = ABSURL(realEntryPath);
  const unknownRawWorkspace: RawWorkspace | RawMultiWorkspace = await (
    await fetch(entryURL)
  ).json();

  // 多工作空间
  if (Array.isArray(unknownRawWorkspace)) {
    validate(unknownRawWorkspace, "multiple");

    // 根据path获取所有未解析的工作空间
    const rawWorkspaces: RawWorkspace[] = [];
    for (const path of unknownRawWorkspace) {
      const realPath = resolvePathname(path, INDEX_HTML_PATH);
      const url = ABSURL(realPath);
      const rw = await (await fetch(url)).json();
      rawWorkspaces.push({
        ...rw,
        path: realPath,
      });
    }

    // 进行当前空间的选择
    let index = Number(
      new URLSearchParams(window.location.search).get("index")
    );
    if (isNaN(index) || index < 0 || index > rawWorkspaces.length - 1) {
      index = 0;
    }

    // 解析当前工作空间
    const workspace = await parseRawWorkspace(rawWorkspaces[index]);

    return {
      workspace,
      rawWorkspaces,
    };
  } else {
    unknownRawWorkspace.path = realEntryPath;
    const workspace = await parseRawWorkspace(unknownRawWorkspace);
    return {
      workspace,
      rawWorkspaces: [unknownRawWorkspace],
    };
  }
}

/** 解析一个资源——对import语句进行解析 */
function parseResource(resource: Resource) {
  const { name, import: _import, ...rest } = resource;

  const declaration = parseImportDeclaration(_import);
  if (declaration.specifiers.length === 0) {
    throw new Error(`请确保import语句至少含有一个导入变量：${resource.import}`);
  }

  const ret: ParsedResource = {
    ...rest,
    id: uid(),
    name: resource.name,
    import: declaration,
  };

  return ret;
}

/** 解析模板 */
async function resolveTemplates(
  templates: RawWorkspace["templates"],
  workspace: Workspace
) {
  const tasks: Promise<string>[] = templates.map(async ([, path]) => {
    const url = ABSURL(resolvePathname(path, workspace.path));
    const res = await fetch(url);
    return await res.text();
  });
  const tempContents: string[] = await Promise.all(tasks);
  return tempContents.map<ParsedTemplate>((content, index) => {
    return {
      id: uid(),
      name: templates[index][0],
      content,
    };
  });
}

/** 解析一个原始工作空间 */
async function parseRawWorkspace(rawWorkspace: RawWorkspace) {
  validate(rawWorkspace, "single");

  const {
    path,
    templates,
    components = [],
    regExps = [],
    validators = [],
    includedBuiltInComponents,
    excludedBuiltInComponents,
    ...rest
  } = rawWorkspace;

  const ws: Workspace = {
    ...rest,
    path,
    templates: [],
    components: [],
    // 解析验证规则
    regExps: regExps.map(parseResource) as ParsedRegularExp[],
    validators: validators.map(parseResource),
  };

  // 解析模板
  ws.templates = await resolveTemplates(templates, ws);

  // 解析组件
  const builtInComponents = mapToSystemDriver(ws.ui).components;
  ws.components = [...builtInComponents, ...components].map((c) => ({
    custom: c.custom ?? true,
    ...parseResource(c),
  }));

  // 组件过滤
  if (includedBuiltInComponents) {
    ws.components = ws.components.filter(
      (c) =>
        c.custom ||
        includedBuiltInComponents!.includes(getFirstVariable(c.import)!)
    );
  } else if (excludedBuiltInComponents) {
    ws.components = ws.components.filter(
      (c) =>
        c.custom ||
        !excludedBuiltInComponents!.includes(getFirstVariable(c.import)!)
    );
  }

  return ws;
}

export function switchWorkspace(targetPath: string) {
  if (window.confirm("切换工作空间会刷新页面，是否继续？")) {
    const { rawWorkspaces } = useWorkspace();
    const index = rawWorkspaces.findIndex((c) => c.path == targetPath);
    const sp = new URLSearchParams(window.location.search);
    sp.set("index", String(index));
    window.location.search = sp.toString();
  }
}
